package test.workload;

/**
 * @author weihuixin
 * @date 2024/2/29
 * @Description
 */

import dm.ast.DmQuery;
import dm.ast.model.gen.ASTGenBase;
import dm.ast.model.gen.QueryGen;
import dm.ast.model.gen.SelectGen;
import dm.ast.fix.FixRunError;
import dm.sql.comp.SQLComplexity;
import test.envset.GlobalState;
import test.log.SqlLog;
import test.stat.StatError;
import test.stat.StatExecTime;
import test.utility.Randomly;
import test.utility.Utility;

/**
 * 生成SQL的工作线程
 */
public class GenWorker extends Worker {
    private static int MAX_BUFFER_LOGS = 102;
    private String infoPre = "";
    int saveFlag = 1;
    boolean execFlag = true;

    private DmQuery dmQuery=null;
    private SqlLog sqlLog = null;
    private long gened_cnt = 0;

    private String execSql=null;
    private long seed = -1;
    private SQLComplexity.ComplexLevel comp = null;

    //2个sql日志缓冲区轮流记录日志
    StringBuilder logBuffer[] = new StringBuilder[2];
    private int logBufIndex = 0;
    private int logs_cnt = 0;
    private String log_head = "";

    //统计相关
    private StatExecTime statTime = new StatExecTime();
    private StatError statError = new StatError();

    public GenWorker(int id , GlobalState globalState){
        this.id = id ;
        this.globalState = globalState;
        this.saveFlag = GlobalState.getGenSaveFlag();
        this.execFlag = GlobalState.getExecFlag();
        if(saveFlag != 0){
            sqlLog = new SqlLog(GlobalState.getLogPre() + "-" + String.valueOf(id) , "sql");
            sqlLog.setAutoSwitch(true);
        }else{
            infoPre = String.format("%s-%d:\n" , INFO_PRE , this.id);
        }

        logBuffer[0] = new StringBuilder();
        logBuffer[1] = new StringBuilder();
    }

    public static void setMaxBufferLog(int workers){
        if(workers == 1){
            MAX_BUFFER_LOGS = 102;
        }else{
            MAX_BUFFER_LOGS = SelectGen.getMaxUsedTimes();
        }
    }

    public StatExecTime getStatExecTime() {
        return statTime;
    }

    public StatError getStatError() {
        return statError;
    }

    //生成sql的具体流程
    @Override
    public void work(){
        ASTGenBase.init(globalState);
        QueryGen queryGen = new QueryGen();  //创建SelectGen对象
        seed = globalState.getRandomly().getSeed();
        comp = GlobalState.getGenComplex();
        setLogHead(comp.toString());
        boolean execSucc = false;
        for(gened_cnt = 0 ; !GlobalState.isGenFinished(gened_cnt) ; gened_cnt++){
            dmQuery = (DmQuery) queryGen.gen(0,comp);
            dmQuery.fill(globalState);
            execSql=dmQuery.toSql();
            counter[0]++;
            if(saveFlag != 2){  //保存所有sql
                log(execSql+ SqlLog.SQL_END);
            }

            if(execFlag){
                execSucc = globalState.executeSelect(execSql);
                if(execSucc){
                    counter[1]++;
                    statTime.addSQLStat(execSql , globalState.getSqlRun());
                }else {
                    //此处尝试修正sql
                    if(FixRunError.fixQueryErrors(dmQuery , globalState.getSqlRun().getErrCode())){
                        execSql=dmQuery.toSql();
                        if(saveFlag != 2){  //保存sql
                            log(execSql + SqlLog.SQL_END);
                        }
                        execSucc = globalState.executeSelect(execSql);
                        if(execSucc){  //修正sql后执行成功
                            counter[1]++;
                            statTime.addSQLStat(execSql , globalState.getSqlRun());
                        }else { // 修正sql后执行失败
                            counter[2]++;
                            statError.addSQLStat(execSql , globalState.getSqlRun());
                        }
                    }else { //未修正sql
                        counter[2]++;
                        statError.addSQLStat(execSql , globalState.getSqlRun());
                    }
                }
            }
            if(saveFlag == 2){
                if(execSucc || //仅保存执行成功的sql
                        GlobalState.getTerminateFlag()){  //如果该sql可能是导致网络异常的错误，也需要记录
                    log(dmQuery.toSql() + SqlLog.SQL_END);
                }
            }
            if(queryGen.getUseTimes() == 0){
                seed = Randomly.getNonCachedInteger();
                globalState.setRandomSeed(seed);
                comp = GlobalState.getGenComplex();
                setLogHead(comp.toString());
            }
        }
        finish();
    }
    public void finish(){
        if(saveFlag != 0){  //保存SQL的情况下，需要关闭各自的SQL日志
            logFlush(sqlLog);
            sqlLog.Close();
        }
        //如果是异常终止，讲所有线程的最后执行sql都合并记录到一个sql文件
        //方便找到导致服务器异常的sql
        if(GlobalState.getTerminateFlag()){
            combineLogBuffer();  // 2个缓冲区的日志都要记录
            logFlush(GlobalState.getSqlLog());
        }
    }

    public synchronized void log(String message){
        switchLogBuf();
        logs_cnt++;
        logBuffer[logBufIndex].append(message);
    }

    //强制当前日志刷盘
    @Override
    public synchronized void logFlush(SqlLog log){
        if(log != null && logBuffer[logBufIndex].length() != 0){
            log.sqlLogMessage(logBuffer[logBufIndex].toString());
        }
    }

    private void setLogHead(String comp){
        log_head = String.format("%s--time:%S\n--ComplexLevel:%s\n" , infoPre , Utility.CurrentTime() , comp);
        if(logs_cnt < MAX_BUFFER_LOGS){  //日志切换缓冲区的末尾不需要增加head，因为切换日志后会增加head
            logBuffer[logBufIndex].append(log_head);
        }
    }

    //切换日志
    //0^1 = 1
    //1^1 = 0
    private synchronized void switchLogBuf(){
        if(logs_cnt >= MAX_BUFFER_LOGS){
            logFlush(sqlLog);
            logBufIndex ^= 1;
            logs_cnt = 0;
            logBuffer[logBufIndex].setLength(0);  //清空将要使用的缓冲区
            setLogHead(comp.toString());
        }
    }

    //合并2个logbuffer ,并将logBufIndex指向合并后的logBuffer
    private synchronized void combineLogBuffer(){
        int tmpLogInd = logBufIndex ^ 1;
        logBuffer[tmpLogInd].append(logBuffer[logBufIndex]);
        logBufIndex = tmpLogInd;
    }
}
